package com.fzkj.nio.chat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * 服务端
 */
public class Server {

    /** 选择器 */
    private Selector selector;
    /** 服务器通道 */
    private ServerSocketChannel ssChannel;
    /** 端口 */
    private static final int PORT = 2323;

    public Server(){
        init();
    }

    /**
     * 初始化方法
     */
    private void init(){
        try{
            // 1、创建选择器对象
            selector = Selector.open();
            // 2、获取通道
            ssChannel = ServerSocketChannel.open();
            // 绑定端口
            ssChannel.bind(new InetSocketAddress(PORT));
            // 3、设置为非阻塞模式
            ssChannel.configureBlocking(false);
            // 4、将通道绑定到选择器上，监听接入事件
            ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 监听事件
     */
    private void listen(){
        try{
            while((selector.select()) > 0){
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                while(it.hasNext()){
                    // 获取当前事件
                    SelectionKey sk = it.next();
                    if (sk.isAcceptable()){ // 判断事件类型
                        // 将当前事件添加到选择器中，并监听读事件
                        SocketChannel sChannel = ssChannel.accept();
                        // 变为非阻塞模式
                        sChannel.configureBlocking(false);
                        // 注册到选择器中，监听读事件
                        sChannel.register(selector, SelectionKey.OP_READ);
                    }else if (sk.isReadable()){
                        // 处理客户端消息，并且转发给各个客户端
                        readClientData(sk);
                    }
                    // 处理完毕之后，移除当前事件
                    it.remove();
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 处理客户端通道的信息，转发给其他客户端
     * @param sk
     */
    private void readClientData(SelectionKey sk) {
        SocketChannel sChannel = null;
        try{
            // 得到当前客户端通道
            sChannel = (SocketChannel) sk.channel();
            // 创建缓冲区对象接收客户端通道数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = sChannel.read(buffer);
            if (count > 0){
                buffer.flip();
                // 提取消息
                String msg = new String (buffer.array(), 0, buffer.remaining());
                System.out.println("接收到客户端消息：" + msg);
                // 转发给其他客户端
                sendMsgToClient(msg, sChannel);
            }
        }catch (Exception e){
            // 当前客户端离线
            try {
                System.out.println("客户端离线：" + sChannel.getRemoteAddress());
                sk.cancel(); // 取消注册
                sChannel.close(); //
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * 转发消息
     * @param msg
     * @param sChannel
     */
    private void sendMsgToClient(String msg, SocketChannel sChannel) throws IOException {
        System.out.println("转发消息");
        for (SelectionKey key : selector.keys()) {
            Channel channel = key.channel();
            // 不转发给自己
            if (channel instanceof SocketChannel && channel != sChannel) {
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                ((SocketChannel)channel).write(buffer);
            }

        }
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.listen();
    }

}
